//日志器模块(建造者模式)
//功能:对之前模块进行整合,向外提供接口完成不同等级的输出

//管理的成员:
//1.格式化模块对象
//2.落地模块对象数组(一个日志器可能会向多个位置进行日志输出)
//3.默认的输出限制等级(大于等于默认限制等级的才能输出)
//4.互斥锁(保证日志输出过程是线程安全的,不会出现交叉日志)
//5.日志器名称(日志器的唯一标识,以便于查找)

//提供的操作(风别会封装日志消息LogMsg--各个接口日志等级不同)
//DEBUG等级日志的输出操作
//INFO等级日志的输出操作
//WARN等级日志的输出操作
//ERROR等级日志的输出操作
//FATAL等级日志的输出操作

//实现:
//1.抽象Logger基类(派生出同步日志器类 & 异步日志器类)
//2.因为两种不同的日志器,只有落地操作不同,因此将落地操作给抽象出来,不同的日志器调用各自的落地操作进行落地日志
//模块关联中使用基类指针对子类日志器对象进行日志管理操作
#ifndef __M_LOGGER_H__
#define __M_LOGGER_H__

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include<atomic>
#include<thread>
#include<mutex>
#include<vector>
#include<cstdarg>
#include<unordered_map>
#include<cassert>
#include"sink.hpp"
#include"level.hpp"
#include"util.hpp"
#include"format.hpp"
#include"message.hpp"
#include"looper.hpp"
#include"buffer.hpp"
namespace zllog
{

    class Logger
    {
    public:
        using ptr = std::shared_ptr<Logger>;
        Logger(FormatMatter::ptr& formatter,std::vector<LogSink::ptr>& sinks,\
            LogLevel::value limit_level,std::string& logger_name)
            :_formatter(formatter)
            ,_sinks(sinks.begin(),sinks.end())
            ,_limit_level(limit_level)
            ,_logger_name(logger_name)
        {}
        const std::string& name()          //获取日志器名称,+const防止在外部被修改
        {
            return _logger_name;
        }
        //完成构造日志消息对象过程并进行格式化,得到格式化之后的日志消息字符串--然后落地进行输出
        void debug(const std::string& file,size_t line,const std::string& fmt,...)
        {
            //通过传入的参数构造出一个日志消息对象,进行日志格式化,最终落地
            //1.判断当前的日志是否到达了输出等级
            if(LogLevel::value::DEBUG < _limit_level)
            {
                return;
            }
            //2.对fmt格式化字符串和不定参进行字符串组织,得到日志消息的字符串
            va_list ap;
            va_start(ap,fmt);
            char* res;
            int ret = vasprintf(&res,fmt.c_str(),ap);
            if(ret == -1)
            {
                std::cout<<"vasprintf failed!"<<std::endl;
                return;
            }
            va_end(ap);   //将ap指针置空
            serialize(LogLevel::value::DEBUG,file,line,res);
            free(res);    //释放开辟的空间
        }
        void info(const std::string& file,size_t line,const std::string& fmt,...)
        {
            if(LogLevel::value::INFO < _limit_level)
            {
                return;
            }
            va_list ap;
            va_start(ap,fmt);
            char* res;
            int ret = vasprintf(&res,fmt.c_str(),ap);
            if(ret == -1)
            {
                std::cout<<"vasprintf failed!"<<std::endl;
                return;
            }
            va_end(ap);   //将ap指针置空
            serialize(LogLevel::value::INFO,file,line,res);
            free(res);    //释放开辟的空间
        }
        void warn(const std::string& file,size_t line,const std::string& fmt,...)
        {
            if(LogLevel::value::WARN < _limit_level)
            {
                return;
            }
            va_list ap;
            va_start(ap,fmt);
            char* res;
            int ret = vasprintf(&res,fmt.c_str(),ap);
            if(ret == -1)
            {
                std::cout<<"vasprintf failed!"<<std::endl;
                return;
            }
            va_end(ap);   //将ap指针置空
            serialize(LogLevel::value::WARN,file,line,res);
            free(res);    //释放开辟的空间
        }
        void error(const std::string& file,size_t line,const std::string& fmt,...)
        {
            if(LogLevel::value::ERROR < _limit_level)
            {
                return;
            }
            va_list ap;
            va_start(ap,fmt);
            char* res;
            int ret = vasprintf(&res,fmt.c_str(),ap);
            if(ret == -1)
            {
                std::cout<<"vasprintf failed!"<<std::endl;
                return;
            }
            va_end(ap);   //将ap指针置空
            serialize(LogLevel::value::ERROR,file,line,res);
            free(res);    //释放开辟的空间
        }
        void fatal(const std::string& file,size_t line,const std::string& fmt,...)
        {
            if(LogLevel::value::FATAL < _limit_level)
            {
                return;
            }
            va_list ap;
            va_start(ap,fmt);
            char* res;
            int ret = vasprintf(&res,fmt.c_str(),ap);
            if(ret == -1)
            {
                std::cout<<"vasprintf failed!"<<std::endl;
                return;
            }
            va_end(ap);   //将ap指针置空
            serialize(LogLevel::value::FATAL,file,line,res);
            free(res);    //释放开辟的空间
        }
    protected:
        void serialize(LogLevel::value level,const std::string& file,size_t line,char* str)
        {
             //3.构造LogMsg对象
             LogMsg msg(level,file,line,str,_logger_name);
             //4.通过格式化工具对LogMsg进行格式化,得到格式化之后的日志字符串
             std::stringstream ss;
             _formatter->format(ss,msg);
             //5.进行日志落地
             log(ss.str().c_str(),ss.str().size());
        }
        //抽象接口后并完成实际落地输出 -- 不同日志器会有不同的落地方式
        virtual void log(const char* data,size_t len) = 0;
    protected:
        FormatMatter::ptr _formatter;
        std::vector<LogSink::ptr> _sinks;
        std::atomic<LogLevel::value> _limit_level;
        std::string _logger_name;
        std::mutex _mutex;        
    };

    //同步日志器
    class SyncLogger : public Logger
    {
    public:
        SyncLogger(FormatMatter::ptr& formatter,std::vector<LogSink::ptr>& sinks,\
            LogLevel::value limit_level,std::string& logger_name)
            :Logger(formatter,sinks,limit_level,logger_name)
        {}
    protected:
        //同步日志器,是将日志直接通过落地模块句柄进行日志落地
        void log(const char* data,size_t len) override
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if(_sinks.empty())
            {
                return;
            }
            else
            {
                for(auto& sink: _sinks)
                {
                    sink->log(data,len);
                }
            }
        }
    };
    
    //异步日志器
    //1.继承于logger日志器类
    //2.通过异步工作器,进行日志数据的实际落地
    //管理的成员:
    //1.异步工作器
    //完成后,完善日志器建造者,进行日志器安全模式选择,提供异步日志器的创建

    class AsyncLogger : public Logger
    {
    public:
        AsyncLogger(FormatMatter::ptr& formatter,std::vector<LogSink::ptr>& sinks,\
            LogLevel::value limit_level,std::string& logger_name,AsyncType looper_type)
            :Logger(formatter,sinks,limit_level,logger_name)
            ,_looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::realLog,this,\
                std::placeholders::_1),looper_type))

        {}
        void log(const char* data,size_t len)//将数据写入缓冲区
        {
            _looper->push(data,len);

        }
        //实际落地函数(将缓冲区中的数据落地)
        void realLog(Buffer& buf)
        {
            if(_sinks.empty())
            {
                return;
            }
            else
            {
                for(auto& sinks : _sinks)
                {
                    sinks->log(buf.begin(),buf.readAbleSize());
                }
            }
        }
    private:
        AsyncLooper::ptr _looper;
    
    };

    //使用建造者模式来建造日志器,而不用让用户直接去构造日志器,简化用户的使用复杂程度
    //1.抽象一个日志器建造者类
    //  //1.设置日志器类
        //2.将不同类型的日志器的创建放到同一个日志器建造者类中完成
    enum class LoggerType
    {
        LOGGER_SYNC = 1,
        LOGGER_ASYNC = 2
    };

    class LogBuilder
    {
    public:
        using ptr = std::shared_ptr<LogBuilder>;
        LogBuilder()
            :_logger_type(LoggerType::LOGGER_SYNC)
            ,_limit_level(LogLevel::value::DEBUG)
            ,_looper_type(AsyncType::ASYNC_SAFE)
        {}
        void buildLoggerType(LoggerType logger_type)
        {
            _logger_type = logger_type;
        }
        void EnableUnSafeAsync()
        {
            _looper_type = AsyncType::ASYNC_UNSAFE;
        }
        void buildLoggerLevel(LogLevel::value limit_level)
        {
            _limit_level = limit_level;
        }
        void buildLoggerName(const std::string& name)
        {
            _logger_name = name;
        }
        void buildLoggerFormatter(const std::string& pattern = "[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n")  //设置日志格式化字符串的方式
        {
            _formatter = std::make_shared<FormatMatter>(pattern);
        }
        template<typename SinkType,typename ... Args>
        void buildSink(Args&& ...args)
        {
            LogSink::ptr psink = SinkFactory::create<SinkType>(std::forward<Args>(args)...);
            _sinks.push_back(psink);
        }
        virtual Logger::ptr build() = 0;
    
    protected:
        AsyncType _looper_type;
        LoggerType _logger_type;
        FormatMatter::ptr _formatter;
        std::vector<LogSink::ptr> _sinks;
        LogLevel::value _limit_level;
        std::string _logger_name;
    };

     //2.派生出具体的建造者类---局部日志器的建造者 和 全局日志器的建造者(添加单例管理器之后,将日志器添加全局属性管理)

    class LocalLoggerBuilder :public LogBuilder
    {
    public:
        Logger::ptr build() override
        {
            assert(_logger_name.empty() == false);       //必须有日志器名
            if(_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<FormatMatter>();
            }
            if(_sinks.empty())
            {
                buildSink<StdoutSink>();
            }
            if(_logger_type == LoggerType::LOGGER_ASYNC)
            {
                return std::make_shared<AsyncLogger>(_formatter,_sinks,_limit_level,_logger_name,_looper_type);
            }
            return std::make_shared<SyncLogger>(_formatter,_sinks,_limit_level,_logger_name);
            
        }        
    }; 


    //日志器管理器:
    //作用1.对所有创建的日志器进行管理
    //特性:将管理器设计为单例  --- 懒汉模式
    //作用2.可以在程序的任意位置,获取相同的单例对象,获取其中的日志器进行日志输出
    //扩展:单例管理器创建时,默认先创建一个日志器(用于进行标准输出的打印,方便用户使用)
        //目的:让用户在不创建任何日志器的情况下,也能进行标准的打印,方便使用
    //实现:
    //管理的成员:
    //1.默认日志器
    //2.所管理的哈希表
    //3.互斥锁
    //提供的接口:
    //1.添加日志器管理
    //2.判断是否管理了指定名称的日志器
    //3.获取指定名称的日志器
    //4.获取默认日志器

    class LoggerManager
    {
    public:
        static LoggerManager& getInstance()    //
        {
            static LoggerManager _eton;
            return _eton;
        }
        void addLogger(Logger::ptr& logger)       //将日志器添加到日志器管理器所管理的哈希表中
        {
            if(hasLogger(logger->name()))
            {
                return;
            }
            std::unique_lock<std::mutex> lock(_mutex);
            _loggers.insert(std::make_pair(logger->name(),logger));
        }
        bool hasLogger(const std::string& logger_name)        
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(logger_name);
            if(it == _loggers.end())
            {
                return false;
            }
            return true; 
        }
        Logger::ptr getLogger(const std::string& logger_name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(logger_name);
            if(it == _loggers.end())
            {
                return Logger::ptr();
            }
            return it->second;

        }
        Logger::ptr rootLogger()
        {
            
            return _root_logger;
        }
    private:
        LoggerManager()
        {
            std::unique_ptr<zllog::LogBuilder> builder(new zllog::LocalLoggerBuilder()) ;
            builder->buildLoggerName("root");
            _root_logger = builder->build();
            _loggers.insert(std::make_pair("root",_root_logger));
        }
        ~LoggerManager()
        {}
        LoggerManager(const LoggerManager&) = delete;
    private:
        Logger::ptr _root_logger;    //默认日志器
        std::unordered_map<std::string,Logger::ptr> _loggers;
        std::mutex _mutex;
    };


    //全局日志器建造者---在局部日志器的基础上增加了一个功能,将日志器添加到单例对象中
    class GlobalLoggerBuilder :public LogBuilder
    {
    public:
        Logger::ptr build() override
        {
            assert(_logger_name.empty() == false);       //必须有日志器名
            if(_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<FormatMatter>();
            }
            if(_sinks.empty())
            {
                buildSink<StdoutSink>();
            }
            Logger::ptr logger;
            if(_logger_type == LoggerType::LOGGER_ASYNC)
            {
                logger = std::make_shared<AsyncLogger>(_formatter,_sinks,_limit_level,_logger_name,_looper_type);
            }
            else
            {
                logger = std::make_shared<SyncLogger>(_formatter,_sinks,_limit_level,_logger_name);
            }
            LoggerManager::getInstance().addLogger(logger);
            return logger;
        }        
    }; 

};

#endif
